﻿using Microsoft.AspNetCore.Mvc;
using Surging.Core.ApiGateWay;
using Surging.Core.ApiGateWay.OAuth;
using Surging.Core.CPlatform;
using Surging.Core.CPlatform.Filters.Implementation;
using Surging.Core.CPlatform.Routing;
using Surging.Core.ProxyGenerator;
using Surging.Core.ProxyGenerator.Utilitys;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using GateWayAppConfig = Surging.Core.ApiGateWay.AppConfig;
using System.Reflection;
using ApiGateway.Auth.Dto;
using ApiGateway.Auth.Services;
using Gmmy.DomainExtension;
using Gmmy.Utility;
using Gmmy.Utility.Enums;
using Gmmy.Utility.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Newtonsoft.Json;
using Surging.Core.CPlatform.Utilities;
using Newtonsoft.Json.Linq;
using Surging.Core.Caching;
using Surging.Core.Common.Extensions;
using Surging.Core.CPlatform.Cache;

namespace ApiGateway.Controllers
{
    public class ServicesController : Controller
    {
        private readonly IServiceProxyProvider _serviceProxyProvider;
        private readonly IServiceRouteProvider _serviceRouteProvider;
        private readonly IAuthorizationServerProvider _authorizationServerProvider;
        private readonly IAuthService _authService;
        public ServicesController(IServiceProxyProvider serviceProxyProvider,
            IServiceRouteProvider serviceRouteProvider,
            IAuthorizationServerProvider authorizationServerProvider, IAuthService authService)
        {
            _serviceProxyProvider = serviceProxyProvider;
            _serviceRouteProvider = serviceRouteProvider;
            _authorizationServerProvider = authorizationServerProvider;
            _authService = authService;
        }

        public async Task<ServiceResult<object>> Path([FromServices] IServicePartProvider servicePartProvider,
            string path, [FromBody] Dictionary<string, object> model)
        {
            ServiceResult<object> result = ServiceResult<object>.Create(false, null);
            string serviceKey = this.Request.Query["servicekey"];
            if (model == null)
            {
                model = new Dictionary<string, object>();
            }

            foreach (string n in this.Request.Query.Keys)
            {
                model[n] = this.Request.Query[n].ToString();
            }
            var authPath = new AuthPath();
            path = path.ToLower() == GateWayAppConfig.TokenEndpointPath.ToLower()
                ? GateWayAppConfig.AuthorizationRoutePath : path.ToLower();
            var proxyResult = new CustomApiResult();
            if (authPath.GetPathDictionary().Values.Contains(path))
            {
                await GetHeaderToken(model);
                if (path == authPath.AuthorizationRoutePath)
                {
                    //为授权登录绑定客户端信息
                    var keyValue = model.FirstOrDefault();
                    dynamic instance = keyValue.Value;
                    if (keyValue.Value == null)
                    {
                        instance = new ExpandoObject();
                    }
                    instance.RequestInfo = JsonConvert.SerializeObject(new
                    {
                        IpAddress = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
                                    HttpContext.Connection.RemoteIpAddress.ToString()
                    });
                    model.Remove(keyValue.Key);
                    model.Add(keyValue.Key, JsonConvert.DeserializeObject(JsonConvert.SerializeObject(instance)));
                    proxyResult = await _authService.Authentication(JsonConvert.DeserializeObject<LoginDto>(model.Values.FirstOrDefault().ToString()));
                }
                else if (path == authPath.AuthorizationUserInfo)
                {
                    proxyResult = await _authService.Info(JsonConvert.DeserializeObject<AuthToken>(model.Values.FirstOrDefault().ToString()));
                }
                else if (path == authPath.AuthorizationLogout)
                {
                    proxyResult = await _authService.Logout(JsonConvert.DeserializeObject<AuthToken>(model.Values.FirstOrDefault().ToString()));
                }
                else if (path == authPath.AuthorizationReflush)
                {
                    proxyResult = await _authService.ReflushAuthToken(JsonConvert.DeserializeObject<AuthToken>(model.Values.FirstOrDefault().ToString()));
                }
                result.IsSucceed = proxyResult.code == CustomApiResultCode.Succ;
                result.StatusCode = proxyResult.code.GetValue();
                result.Entity = proxyResult.data;
                result.Message = string.IsNullOrEmpty(proxyResult.message) ? (proxyResult.code == CustomApiResultCode.AuthenticationInvalid
                    ? "认证凭据无效或已过期"
                    : proxyResult.message) : proxyResult.message;
            }
            else
            {
                if (await GetAllowRequest(path) == false) return new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.RequestError, Message = "Request error" };
                if (servicePartProvider.IsPart(path))
                {
                    result = ServiceResult<object>.Create(true, await servicePartProvider.Merge(path, model));
                    result.StatusCode = (int)CustomApiResultCode.HttpPortErr;
                }
                else
                {
                    var authResult = await OnAuthorization(path, model);
                    if (authResult.isSuccsess)
                    {
                        result = authResult.result;
                        var rpcSuccess = true;
                        var proxyRpcResult = await _serviceProxyProvider.Invoke<object>(model, path,
                            string.IsNullOrEmpty(serviceKey) ? null : serviceKey);
                        if (proxyRpcResult != null)
                        {
                            //由于messagepack对时间处理不友好(时区不能+8),所以只能在这里进行一次json.net反序化
                            proxyResult = JsonConvert.DeserializeObject<CustomApiResult>(proxyRpcResult.ToString());
                        }
                        else
                        {
                            rpcSuccess = false;
                        }
                        if (!rpcSuccess)
                        {
                            if (path == GateWayAppConfig.AuthorizationRoutePath)
                            {
                                result.StatusCode = (int) CustomApiResultCode.AuthenticationInvalid;
                                result.Message = "授权登录失败,请稍后";
                            }
                            else
                            {
                                result.StatusCode = (int) CustomApiResultCode.HttpPortErr;
                                result.Message = "服务异常,请稍后再试";
                            }
                        }
                        else
                        {
                            result.IsSucceed = true;
                            result.StatusCode = proxyResult.code.GetValue();
                            result.Entity = proxyResult.data;
                            result.Message = proxyResult.message;
                        }
                    }
                    else
                    {
                        result.StatusCode = authResult.result.StatusCode;
                        result.Message = authResult.result.Message;
                    }
                }
            }
            return result;
        }

        private async Task<(bool isSuccsess, ServiceResult<object> result)> OnAuthorization(string path, Dictionary<string, object> model)
        {
            var result = new Tuple<bool, ServiceResult<object>>(false, new ServiceResult<object>());
            var route = _serviceRouteProvider.GetRouteByPath(path).Result;
            if (route.ServiceDescriptor.EnableAuthorization())
            {
                if (route.ServiceDescriptor.AuthType() == AuthorizationType.JWT.ToString())
                {
                    return await ValidateJwtAuthentication(route, model, path);
                }
                else
                {
                    return (ValidateAppSecretAuthentication(route, path, model, out ServiceResult<object> obj), obj);
                }
            }
            else
            {
                return await GetHeaderToken(model);
            }
        }

        public async Task<(bool, ServiceResult<object>)> GetHeaderToken(Dictionary<string, object> model)
        {
            //当请求路由不涉及授权检测时，也应该根据Authorization返回给服务节点相关的授权信息
            var author = HttpContext.Request.Headers["Authorization"];
            if (author.Count > 0)
            {
                var keyValue = model.FirstOrDefault();
                dynamic instance = keyValue.Value;
                if (keyValue.Value == null)
                {
                    instance = new ExpandoObject();
                }
                var getUser = await _authService.GetTokenInfo(author[0]);
                if (getUser.isSuccess)
                {
                    instance.AuthInfo = getUser.userInfo;
                    instance.RequestInfo = JsonConvert.SerializeObject(new
                    {
                        IpAddress = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
                                    HttpContext.Connection.RemoteIpAddress.ToString()
                    });
                    instance.Token = author[0];
                    model.Remove(keyValue.Key);
                    model.Add(keyValue.Key, JsonConvert.DeserializeObject(JsonConvert.SerializeObject(instance)));
                }
                else
                {
                    return (false, new ServiceResult<object>()
                    {
                        StatusCode = (int)CustomApiResultCode.AuthenticationInvalid,
                        Message = "认证凭据无效或已过期",
                        IsSucceed = false
                    });
                }
            }
            return (true, new ServiceResult<object>());
        }
        public async Task<(bool, ServiceResult<object>)> ValidateJwtAuthentication(ServiceRoute route, Dictionary<string, object> model, string path)
        {
            var IsSucceed = true;
            var result = new ServiceResult<object>();
            var author = HttpContext.Request.Headers["Authorization"];
            if (author.Count > 0)
            {
                var tokenModel = new Dictionary<string, object>();
                tokenModel.Add("input",
                    JsonConvert.DeserializeObject(JsonConvert.SerializeObject(new {Token = author[0], Path = path})));
                var tokenResult =
                    await _authService.AuthorizationCheck(new UserCheckDto() {Path = path, Token = author[0]});
                if (tokenResult.code == CustomApiResultCode.Succ)
                {
                    var keyValue = model.FirstOrDefault();
                    dynamic instance = keyValue.Value;
                    if (keyValue.Value == null)
                    {
                        instance = new ExpandoObject();
                    }
                    instance.AuthInfo = tokenResult.data;
                    instance.RequestInfo = JsonConvert.SerializeObject(new
                    {
                        IpAddress = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
                                    HttpContext.Connection.RemoteIpAddress.ToString()
                    });
                    instance.Token = author[0];
                    model.Remove(keyValue.Key);
                    model.Add(keyValue.Key, JsonConvert.DeserializeObject(JsonConvert.SerializeObject(instance)));
                    result.IsSucceed = true;
                }
                else
                {
                    if (tokenResult.code == CustomApiResultCode.AuthenticationInvalid)
                        result = new ServiceResult<object>
                        {
                            IsSucceed = false,
                            StatusCode = (int) CustomApiResultCode.AuthenticationInvalid,
                            Message = "认证凭据无效或已过期"
                        };
                    else if (tokenResult.code == CustomApiResultCode.AuthorizationInvalid)
                        result = new ServiceResult<object>
                        {
                            IsSucceed = false,
                            StatusCode = (int) CustomApiResultCode.AuthorizationInvalid,
                            Message = "权限不足"
                        };
                }
            }
            else
            {
                result = new ServiceResult<object>
                {
                    IsSucceed = false,
                    StatusCode = (int) CustomApiResultCode.AuthenticationInvalid,
                    Message = "无效认证凭据"
                };
            }
            return (result.IsSucceed, result);
        }

        private bool ValidateAppSecretAuthentication(ServiceRoute route, string path,
            Dictionary<string, object> model, out ServiceResult<object> result)
        {
            bool isSuccess = true;
            result = new ServiceResult<object>();
            DateTime time;
            var author = HttpContext.Request.Headers["Authorization"];

            if (!string.IsNullOrEmpty(path) && model.ContainsKey("timeStamp") && author.Count > 0)
            {
                if (DateTime.TryParse(model["timeStamp"].ToString(), out time))
                {
                    var seconds = (DateTime.Now - time).TotalSeconds;
                    if (seconds <= 3560 && seconds >= 0)
                    {
                        if (EncryptHelper.GetMd5($"{route.ServiceDescriptor.Token}{time.ToString("yyyy-MM-dd hh:mm:ss") }") != author.ToString())
                        {
                            result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" };
                            isSuccess = false;
                        }
                    }
                    else
                    {
                        result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" };
                        isSuccess = false;
                    }
                }
                else
                {
                    result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" };
                    isSuccess = false;
                }
            }
            else
            {
                result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.RequestError, Message = "Request error" };
                isSuccess = false;
            }
            return isSuccess;
        }

        private async Task<bool> GetAllowRequest(string path)
        {
            var route = await _serviceRouteProvider.GetRouteByPath(path);
            return !route.ServiceDescriptor.DisableNetwork();
        }
    }
}
